package com.yyj.service;

import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.common.collect.Maps;
import com.yyj.common.ConfigReader;
import com.yyj.config.WeixinPayConfig;
import com.yyj.dao.TPropertyPaymentMapper;
import com.yyj.dao.TPropertyWaterBillMapper;
import com.yyj.enums.OrderStatusEnum;
import com.yyj.enums.PaymentStatusEnum;
import com.yyj.enums.PaymentTypeEnum;
import com.yyj.exception.BaseException;
import com.yyj.model.TPropertyPayment;
import com.yyj.model.TPropertyWaterBill;
import com.yyj.model.TPropertyWaterBillExample;
import com.yyj.util.AESUtil;
import com.yyj.util.DateUtil;
import com.yyj.util.StreamUtil;
import com.yyj.util.security.RandomUtil;
import com.yyj.web.protocol.req.PaymentQueryReq;
import com.yyj.web.protocol.req.WxPayQueryReq;
import com.yyj.web.protocol.req.WxPayReq;
import com.yyj.web.protocol.req.WxRefundReq;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author leezp
 * @create 2018-04-10 14:26
 **/

@Service
public class WeixinPayService {

    public static Logger log = LoggerFactory.getLogger(WeixinPayService.class);

    @Autowired
    WeixinPayConfig config;

    @Autowired
    ConfigReader configReader;

    @Autowired
    PaymentService paymentService;

    @Autowired
    TPropertyPaymentMapper paymentMapper;

    @Autowired
    TPropertyWaterBillMapper waterBillMapper;

    @Autowired
    PushMessageService pushMessageService;

    /**
     * 发起微信支付
     * @param wxPayReq
     * @return
     * @throws Exception
     */
    @Transactional
    public Map<String, String> dealWxPay(WxPayReq wxPayReq) throws Exception {

        // 给微信服务器统一下单
        WXPay wxpay = new WXPay(config);
        // 封装入参字典
        Map<String, String> data = new HashMap<>(16);
        // 商品描述
        data.put("body", wxPayReq.getFeeDate() + PaymentTypeEnum.getActivityCode(wxPayReq.getType()).getDesc() + wxPayReq.getMoney());
        // 商户订单号
        data.put("out_trade_no", wxPayReq.getPayNo());
        // 随机字符串
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        // 总金额 分 将BigDecimal乘以100再转为字符串
        String totalFee = wxPayReq.getMoney().multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP).toString();
        data.put("total_fee", totalFee);
        // 终端IP
        data.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress());
        // 接收微信支付异步通知回调地址
        data.put("notify_url", configReader.getBasePath() + "/payments/wxPayNotify");
        // 交易类型
        data.put("trade_type", "JSAPI");
        // 获取openid
        String openId = wxPayReq.getOpenId();
        data.put("openid", openId);

        // 和微信服务器交互，统一下单
        Map<String, String> resp = wxpay.unifiedOrder(data);
        log.info("微信统一下单返回: " + resp);
        String return_code = resp.get("return_code").toString();
        if(StringUtils.isNotBlank(return_code) && "FAIL".equals(return_code)){
            throw new BaseException(resp.get("return_msg").toString());
        }

        // 小程序再次签名
        Map<String, String> data2 = new HashMap<>(16);
        data2.put("appId", resp.get("appid"));
        String timeStamp = System.currentTimeMillis()/1000 + "";
        data2.put("timeStamp", timeStamp);
        String nonceStr =  WXPayUtil.generateNonceStr();
        data2.put("nonceStr", nonceStr);
        String packages = "prepay_id="+resp.get("prepay_id");
        data2.put("package", packages);
        data2.put("signType", "MD5");
        String sign2 = WXPayUtil.generateSignature(data2, configReader.getPayKey());

        // 封装返回给前端map
        Map<String, String> result = new HashMap<>(16);
        result.put("timeStamp", timeStamp);
        result.put("nonceStr", nonceStr);
        result.put("package", packages);
        result.put("signType", "MD5");
        result.put("paySign", sign2);
        result.put("orderNum", wxPayReq.getPayNo());

        log.info("dealWxPay结果: " + result);
        return result;
    }


    /**
     * 微信支付回调，处理业务逻辑
     * @param request
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public String payCallback(HttpServletRequest request) throws Exception {
        // 获取支付结果，XML格式
        String notifyData = new String(StreamUtil.readInStream(request.getInputStream()));
        log.info("payCallback支付结果: " + notifyData);

        WXPay wxPay = new WXPay(config);
        // 支付结果转成map
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
        if (wxPay.isPayResultNotifySignatureValid(notifyMap)) {
            // 签名正确
            log.info("签名正确: " + notifyMap);
            // 获取订单号
            String payNo = notifyMap.get("out_trade_no");
            //微信支付订单号
            String transactionId = notifyMap.get("transaction_id");
            //openId
            String openId = notifyMap.get("openid");
            log.info("订单号：[{}] , 微信支付订单号:[{}] , openId:[{}]" , payNo , transactionId , openId);
            // 获取金额（分）
            String  totalFee = notifyMap.get("total_fee");
            // 转为BigDecimal
            BigDecimal totalFee2 = new BigDecimal(totalFee);
            totalFee2 = totalFee2.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
            log.info("totalFee2>>>>" + totalFee2);

            // 根据订单号获取交易记录,更新交易状态
            PaymentQueryReq paymentQueryReq = new PaymentQueryReq();
            paymentQueryReq.setPayNo(payNo);
            TPropertyPayment paymentObj = paymentService.findPaymentByPayNo(payNo);
            if(null != paymentObj && PaymentStatusEnum.NO_PAY.getCode().equals(paymentObj.getStatus())
                    && OrderStatusEnum.NO_PAY.getCode().equals(paymentObj.getPayStatus())
                    && paymentObj.getAmount().compareTo(totalFee2) == 0
                    ){
                paymentObj.setPayStatus(OrderStatusEnum.IS_PAY.getCode());
                paymentObj.setStatus(PaymentStatusEnum.IS_PAY.getCode());
                paymentObj.setUpdateTime(new Date());
                paymentObj.setPayTime(new Date());
                paymentMapper.updateByPrimaryKeySelective(paymentObj);

                TPropertyWaterBillExample waterBillExample = new TPropertyWaterBillExample();
                TPropertyWaterBillExample.Criteria criteria = waterBillExample.createCriteria();
                criteria.andFeeDateEqualTo(DateUtil.getNowShortDate().substring(0 , 6));
                criteria.andRoomNumEqualTo(paymentObj.getRoomNum());
                criteria.andStatusEqualTo(PaymentStatusEnum.NO_PAY.getCode());
                List<TPropertyWaterBill> waterBilllist = waterBillMapper.selectByExample(waterBillExample);
                if(waterBilllist.size() > 0){
                    TPropertyWaterBill waterBill = waterBilllist.get(0);
                    waterBill.setStatus(PaymentStatusEnum.IS_PAY.getCode());
                    waterBill.setUpdateTime(new Date());
                    waterBillMapper.updateByPrimaryKeySelective(waterBill);
                }
                pushMessageService.pushPayResultToOwners(paymentObj , openId);

                return "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
            }
        } else {
            // 签名错误
            throw new BaseException("签名错误");
        }
        return "<xml><return_code>FALL</return_code><return_msg>失败</return_msg></xml>";
    }


    /**
     * 发起微信支付退款
     * @param wxRefundReq
     * @return
     * @throws Exception
     */
    @Transactional
    public Map<String, String> dealPayRefund(WxRefundReq wxRefundReq) throws Exception {

        WXPay wxpay = new WXPay(config);
        // 封装入参字典
        Map<String, String> data = new HashMap<>(16);
        // 商户订单号
        data.put("out_trade_no", wxRefundReq.getOrderNum());
        //商户退款号
        String refundNum = RandomUtil.digits(16);
        data.put("out_refund_no", refundNum);
        // 随机字符串
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        // 总金额 分 将BigDecimal乘以100再转为字符串
        String totalFee = (new BigDecimal(wxRefundReq.getMoney()).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP)).toString();
        data.put("total_fee", totalFee);
        data.put("refund_fee", totalFee);
        // 退款原因
        data.put("refund_desc", "");
        // 接收微信退款异步通知回调地址
        data.put("notify_url", configReader.getBasePath() + "/pay/refundNotify");

        // 和微信服务器交互，发起退款
        Map<String, String> resp = wxpay.refund(data);
        log.info("微信退款返回: " + resp);
        String return_code = resp.get("return_code").toString();
        if(StringUtils.isNotBlank(return_code) && "FAIL".equals(return_code)){
            throw new BaseException(resp.get("return_msg").toString());
        }else
        if(StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code)&& "SUCCESS".equals(resp.get("result_code").toString())){
            //更新订单状态为退款申请中

        }

        return resp;
    }

    /**
     * 微信支付回调，处理业务逻辑
     * @param request
     * @return
     * @throws Exception
     */
    public String refundCallback(HttpServletRequest request) throws Exception {
        // 获取支付结果，XML格式
        String notifyData = new String(StreamUtil.readInStream(request.getInputStream()));
        log.info("refundCallback退款结果:{} " , notifyData);
        // 支付结果转成map
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
        log.info("notifyMap:{}" , JSONObject.toJSONString(notifyMap));
        String return_code = notifyMap.get("return_code").toString();
        log.info("return_code:{}" , return_code);
        if("SUCCESS".equals(return_code)){
            String req_info = notifyMap.get("req_info").toString();
            log.info("req_info:{}" , req_info);
            String key = configReader.getPayKey();
            String decodeData = AESUtil.decryptData(req_info , key);
            log.info("decodeData:{}" , decodeData);
            Map<String, String> respMap = WXPayUtil.xmlToMap(decodeData);
            log.info("refundMap:{}" , JSONObject.toJSONString(respMap));
            String refund_status = respMap.get("refund_status").toString();
            if("SUCCESS".equals(refund_status)){
                String orderNum = respMap.get("out_trade_no").toString();
                // 根据订单号获取交易记录,更新交易状态

                // 获取金额（分）
                String refundFee = respMap.get("refund_fee");
                // 转为BigDecimal
                BigDecimal totalFee2 = new BigDecimal(refundFee);
                totalFee2 = totalFee2.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
                if (false){

                    return "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
                } else {
                    log.info("订单不存在或者不符合，修改失败");
                }
            }
        }

        return "<xml><return_code>FALL</return_code><return_msg>失败</return_msg></xml>";

    }

    /**
     * 查询微信支付订单状态
     * @param wxPayQueryReq
     * @return
     * @throws Exception
     */
    public Map<String, String> queryWxPayOrder(WxPayQueryReq wxPayQueryReq) throws Exception {
        Map<String, String> resultMap = Maps.newHashMap();

        WXPay wxpay = new WXPay(config);
        // 封装入参字典
        Map<String, String> data = new HashMap<>(16);
        // 商户订单号
        data.put("out_trade_no", wxPayQueryReq.getOrderNum());
        // 随机字符串
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        Map<String, String> resp = wxpay.orderQuery(data);
        log.info("查询微信支付订单返回: " + resp);
        String return_code = resp.get("return_code").toString();
        if(StringUtils.isNotBlank(return_code) && "FAIL".equals(return_code)){
            throw new BaseException(resp.get("return_msg").toString());
        }else
        if(StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code)){
            resultMap.put("tradeState" , resp.get("trade_state").toString());
            resultMap.put("tradeStateDesc" , resp.get("trade_state_desc").toString());
        }

        return resultMap;
    }

    /**
     * 查询微信支付退款订单状态
     * @param wxPayQueryReq
     * @return
     * @throws Exception
     */
    public Map<String, String> queryWxRefundOrder(WxPayQueryReq wxPayQueryReq) throws Exception {
        Map<String, String> resultMap = Maps.newHashMap();

        WXPay wxpay = new WXPay(config);
        // 封装入参字典
        Map<String, String> data = new HashMap<>(16);
        // 商户订单号
        data.put("out_trade_no", wxPayQueryReq.getOrderNum());
        // 随机字符串
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        Map<String, String> resp = wxpay.refundQuery(data);
        log.info("查询微信支付退款订单返回: " + resp);
       /* String return_code = resp.get("return_code").toString();
        if(StringUtils.isNotBlank(return_code) && "FAIL".equals(return_code)){
            throw new BaseException(resp.get("return_msg").toString());
        }else
        if(StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code)){
            resultMap.put("tradeState" , resp.get("trade_state").toString());
            resultMap.put("tradeStateDesc" , resp.get("trade_state_desc").toString());
        }*/

        return resp;
    }


    public static void main(String[] args) {
        System.out.println(DateUtil.getCron(new Date()));
        String notifyData = "<xml><return_code>SUCCESS</return_code><appid><![CDATA[wxe3d49b5b7bc36313]]></appid><mch_id><![CDATA[1493584302]]></mch_id><nonce_str><![CDATA[36f15a47c4621c1f03ebba7f93942b9e]]></nonce_str><req_info><![CDATA[depuuU3xMzEe5boCXbUxLefFXxr0M8Jw96QGQzjSefmG72azhe25/P3+OfbzE6Zq90JJsn6aTNW3HfTyttYPbwsfx2auGfOUVNkeipK5rE2y1GA5fxuIV1MtMsgy7uH1YEuxPlzb3Diul+eN8UhwMnmWaKM/iX4jn2hk0D+9mAmfZ3hLwJTx7EOhYqHi+Up0eOV9jbXw7aG5WiGljh+0RrHzPtYQqPevMwTpDzDbfOBuVGhEPmBoIMHb0kKg255DrKhdnCnbqGsflpAyIb3JuE+weALLLReNT1BEN0s54PxR0+5EYnmvBYkRnZQUORv87qr9vJ9RUo/Wcewk/c5/e0jdRqE65gb43wu38s8uCC0u0Pu4LxTNJZ7v3xe9WsOoZeT9zmSjB/7UnriPnDPXu5nrO7qqMyZVjlT6vWmvLK4ZVoEjgOa94qhKRbnboVmtpZfINhwVguDTCZWEroNeUQ++vXTyb+3WiNv0CADREGB3Gx5lgmjfHPhvfg1vGsMjek2tjykPgO4rSf/kolE7eFZ9Zbzy1RY1xC6bmOxcjvWTqVkive+eAqlzOF5fNiA6wK6Qa+I9RuYM9l9Q5rrwe8BCVcmGpXI9XFwWL+sgvmKrHnixummn7zmI91vTUXH8X/BELsAIPGlTsCWGIbq92IZrbXnrCt+agdVSkIv44kDqA1qyN6hdfQ4WcRnK496ONQEONo1TrEzgkSEVeIrEmKwFGD5I6elBTTkAhfVBSTIry18N+OFnpt8BvfDsh+lfHiKmLJfTYHfclwlvZjc0eaeSjSwOYFUGNK+rCoC0ge3WaVIVmKbZnY6Dm20aexmbCKNzN7n29VxhotElRPxPTSEDa3bmPu+NrO4M5VFBHxbB8ENKp3TlvpwbYJOx8SQQU9qQ8DBQ9V9VjOCDnre/zNQkxLRSJTlg/GQyZTbUxfTWvbRRPVpQlH/rzAYSzftijyQBpulvA5+9Ty9zxLeteApMulOzBV5Q2r/YqeKJ3JmGb/u4ETIUKEchwOHQAbWNJ79EnDAtHPfxzdZRmulGGa+FjRmxu3If0RXQUcSUS0jznu/ptnvt2UdarvoG9lLb]]></req_info></xml>";
        try {
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);
            System.out.printf(JSONObject.toJSONString(notifyMap));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
